﻿////////////////////////////////////////////////////////////////////////
//                                                                    //
//                 Project: MediaElement Extensions                   //
//               Project URL: http://mext.codeplex.com                //
//                           GPL License v2                           //
//                                                                    //
//             Written by Geoffrey DANIEL { NeuroCypher }             //
//           Blog: http://blogs.developpeur.org/neurocypher           //
//                                                                    //
////////////////////////////////////////////////////////////////////////

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Media;
using System.Windows.Threading;
using MediaElementExtensions.Controls.Bases;

namespace MediaElementExtensions.Controls
{

    public class ProgressSlider : SliderMediaElementExtensionControlBase
    {

        #region Members

        private DispatcherTimer _timer = null;
        private bool _isSliderDragging = false;
        private ScaleTransform _downloadScale = null;
        private double _refreshFrequency = 50;

        #endregion

        #region DependencyProperties

        #region Thumb

        // Thumb Height
        public static DependencyProperty ThumbHeightProperty =
            DependencyProperty.Register("ThumbHeight", typeof(double), typeof(ProgressSlider), null);

        public double ThumbHeight
        {
            get { return (double)GetValue(ThumbHeightProperty); }
            set { SetValue(ThumbHeightProperty, value); }
        }

        // Thumb Width
        public static DependencyProperty ThumbWidthProperty =
            DependencyProperty.Register("ThumbWidth", typeof(double), typeof(ProgressSlider), null);

        public double ThumbWidth
        {
            get { return (double)GetValue(ThumbWidthProperty); }
            set { SetValue(ThumbWidthProperty, value); }
        }

        // Thumb Background
        public static DependencyProperty ThumbBackgroundBrushProperty =
        DependencyProperty.Register("ThumbBackgroundBrush", typeof(Brush), typeof(ProgressSlider), null);

        public Brush ThumbBackgroundBrush
        {
            get { return (Brush)GetValue(ThumbBackgroundBrushProperty); }
            set { SetValue(ThumbBackgroundBrushProperty, value); }
        }

        // Thumb Stroke
        public static DependencyProperty ThumbStrokeBrushProperty =
            DependencyProperty.Register("ThumbStrokeBrush", typeof(Brush), typeof(ProgressSlider), null);

        public Brush ThumbStrokeBrush
        {
            get { return (Brush)GetValue(ThumbStrokeBrushProperty); }
            set { SetValue(ThumbStrokeBrushProperty, value); }
        }

        // Thumb Stroke Thickness
        public static DependencyProperty ThumbStrokeThicknessProperty =
            DependencyProperty.Register("ThumbStrokeThickness", typeof(Thickness), typeof(ProgressSlider), null);

        public Thickness ThumbStrokeThickness
        {
            get { return (Thickness)GetValue(ThumbStrokeThicknessProperty); }
            set { SetValue(ThumbStrokeThicknessProperty, value); }
        }

        #endregion

        #region DownloadLayer

        // Download Layer Height
        public static DependencyProperty DownloadLayerHeightProperty =
            DependencyProperty.Register("DownloadLayerHeight", typeof(double), typeof(ProgressSlider), null);

        public double DownloadLayerHeight
        {
            get { return (double)GetValue(DownloadLayerHeightProperty); }
            set { SetValue(DownloadLayerHeightProperty, value); }
        }

        // Download Layer Brush
        public static DependencyProperty DownloadLayerBrushProperty =
            DependencyProperty.Register("DownloadLayerBrush", typeof(Brush), typeof(ProgressSlider), null);

        public Brush DownloadLayerBrush
        {
            get { return (Brush)GetValue(DownloadLayerBrushProperty); }
            set { SetValue(DownloadLayerBrushProperty, value); }
        }

        // Download Layer Stroke Thickness
        public static DependencyProperty DownloadLayerStrokeThicknessProperty =
            DependencyProperty.Register("DownloadLayerStrokeThickness", typeof(Thickness), typeof(ProgressSlider), null);

        public Thickness DownloadLayerStrokeThickness
        {
            get { return (Thickness)GetValue(DownloadLayerStrokeThicknessProperty); }
            set { SetValue(DownloadLayerStrokeThicknessProperty, value); }
        }

        // Download Layer Stroke Brush
        public static DependencyProperty DownloadLayerStrokeBrushProperty =
            DependencyProperty.Register("DownloadLayerStrokeBrush", typeof(Brush), typeof(ProgressSlider), null);

        public Brush DownloadLayerStrokeBrush
        {
            get { return (Brush)GetValue(DownloadLayerStrokeBrushProperty); }
            set { SetValue(DownloadLayerStrokeBrushProperty, value); }
        }

        #endregion

        #region TrackLayer

        // Track Layer Height
        public static DependencyProperty TrackLayerHeightProperty =
            DependencyProperty.Register("TrackLayerHeight", typeof(double), typeof(ProgressSlider), null);

        public double TrackLayerHeight
        {
            get { return (double)GetValue(TrackLayerHeightProperty); }
            set { SetValue(TrackLayerHeightProperty, value); }
        }

        // Track Layer Brush
        public static DependencyProperty TrackLayerBrushProperty =
            DependencyProperty.Register("TrackLayerBrush", typeof(Brush), typeof(ProgressSlider), null);

        public Brush TrackLayerBrush
        {
            get { return (Brush)GetValue(TrackLayerBrushProperty); }
            set { SetValue(TrackLayerBrushProperty, value); }
        }

        // Track Layer Disabled Brush
        public static DependencyProperty TrackLayerDisabledBrushProperty =
            DependencyProperty.Register("TrackLayerDisabledBrush", typeof(Brush), typeof(ProgressSlider), null);

        public Brush TrackLayerDisabledBrush
        {
            get { return (Brush)GetValue(TrackLayerDisabledBrushProperty); }
            set { SetValue(TrackLayerDisabledBrushProperty, value); }
        }

        // Track Layer Stroke Thickness
        public static DependencyProperty TrackLayerStrokeThicknessProperty =
            DependencyProperty.Register("TrackLayerStrokeThickness", typeof(Thickness), typeof(ProgressSlider), null);

        public Thickness TrackLayerStrokeThickness
        {
            get { return (Thickness)GetValue(TrackLayerStrokeThicknessProperty); }
            set { SetValue(TrackLayerStrokeThicknessProperty, value); }
        }

        // Track Layer Stroke Brush
        public static DependencyProperty TrackLayerStrokeBrushProperty =
            DependencyProperty.Register("TrackLayerStrokeBrush", typeof(Brush), typeof(ProgressSlider), null);

        public Brush TrackLayerStrokeBrush
        {
            get { return (Brush)GetValue(TrackLayerStrokeBrushProperty); }
            set { SetValue(TrackLayerStrokeBrushProperty, value); }
        }

        #endregion

        #endregion

        #region Properties

        #region Bridling

        public new double Minimum
        {
            get { return base.Minimum; }
            private set { base.Minimum = value; }
        }

        public new double Maximum
        {
            get { return base.Maximum; }
            private set { base.Maximum = value; }
        }

        public new Orientation Orientation
        {
            get { return base.Orientation; }
            private set { base.Orientation = value; }
        }

        public new double Value
        {
            get { return base.Value; }
            private set { base.Value = value; }
        }

        #endregion

        #region Download Scaling

        private double DownloadScaleX
        {
            set
            {
                if (_downloadScale != null)
                    _downloadScale.ScaleX = value;
            }
        }

        #endregion

        #region Refresh / Timer

        public double RefreshFrequencyMilliseconds
        {
            get { return _refreshFrequency; }
            set
            {
                _refreshFrequency = value;

                if (value >= 0 && _timer != null)
                {
                    _timer.Interval = TimeSpan.FromMilliseconds(value);
                }
            }
        }

        #endregion

        #endregion

        #region Constructor

        public ProgressSlider()
        {

            InitializeActions(
                CheckAllSubActions,
                (s, e) => CheckAllSubActions(),
                (s, e) => SetPositionDownload(),
                (s, e) => CheckAllSubActions(),
                (s, e) => CheckAllSubActions(),
                (s, e) => CheckAllSubActions());

            InitializeTimer();

            CheckElements();

            DefaultStyleKey = typeof(ProgressSlider);

        }

        #endregion

        #region OnApplyTemplate

        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();

            var thumb = GetTemplateChild("HorizontalThumb") as Thumb;
            if (thumb != null)
            {
                thumb.DragStarted += (s, e) => _isSliderDragging = true;
                thumb.DragCompleted += (s, e) =>
                {
                    _isSliderDragging = false;
                    SeekMedia(Value);
                };
            }

            var leftSide = GetTemplateChild("PART_LeftSide") as FrameworkElement;
            if (leftSide != null)
                leftSide.MouseLeftButtonDown += (s, e) => MouseSeek(e.GetPosition(this));

            var rightSide = GetTemplateChild("PART_RightSide") as FrameworkElement;
            if (rightSide != null)
                rightSide.MouseLeftButtonDown += (s, e) => MouseSeek(e.GetPosition(this));

            var downloadLayer = GetTemplateChild("PART_DownloadLayer") as FrameworkElement;
            if (downloadLayer != null)
                _downloadScale = downloadLayer.RenderTransform as ScaleTransform;

        }

        #endregion

        #region Sub Actions

        private void CheckTimerActivation()
        {

            if (MediaElement == null ||
                MediaElement.CurrentState == MediaElementState.Closed)
            {
                _timer.Stop();
            }
            else
            {
                _timer.Start();
            }

        }

        private void CheckElements()
        {

            if (MediaElement == null ||
                MediaElement.CurrentState == MediaElementState.Closed)
            {
                Value = 0;
                DownloadScaleX = 0;
            }
            else
            {
                SetPositionValue();
                SetPositionDownload();
            }

        }

        private void CheckAllSubActions()
        {

            CheckIsEnabled();
            CheckTimerActivation();
            CheckElements();

        }

        #endregion

        #region Control Elements Management

        private void MouseSeek(Point position)
        {

            Value = position.X / ActualWidth;
            SeekMedia(Value);

        }

        private void SeekMedia(double percentPosition)
        {

            if (MediaElement != null &&
                MediaElement.CurrentState != MediaElementState.Closed)
            {
                // The player cannot display a part which has not been downloaded
                var min = Math.Min(percentPosition, MediaElement.DownloadProgress - 0.01);

                MediaElement.Position = TimeSpan.FromMilliseconds(MediaElement.NaturalDuration.TimeSpan.TotalMilliseconds * min);
            }
            else
            {
                CheckAllSubActions();
            }

        }

        private void SetPositionValue()
        {

            if (MediaElement == null ||
                MediaElement.CurrentState == MediaElementState.Closed)
            {
                CheckAllSubActions();
            }
            else if (_isSliderDragging || MediaElement.NaturalDuration.TimeSpan.TotalMilliseconds <= 0)
            {
                return;
            }
            else
            {
                Value = MediaElement.Position.TotalMilliseconds / MediaElement.NaturalDuration.TimeSpan.TotalMilliseconds;
            }

        }

        private void SetPositionDownload()
        {

            if (MediaElement == null ||
                MediaElement.CurrentState == MediaElementState.Closed)
            {
                CheckAllSubActions();
            }
            else
            {
                DownloadScaleX = MediaElement.DownloadProgress;
            }

        }

        #endregion

        #region Timer Management

        private void InitializeTimer()
        {

            _timer = new DispatcherTimer();
            _timer.Interval = TimeSpan.FromMilliseconds(RefreshFrequencyMilliseconds);
            _timer.Tick += (s, e) => SetPositionValue();

        }

        #endregion

    }

}
